package cn.com.franke.io;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.System.err;
import static java.lang.System.out;

/**
 * Created by liuxianzhao on 2017/5/3.
 */
public class FilesTest {

    @Test
    public void test1() {
        try {
            String source = URLDecoder.decode(this.getClass().getResource("/test/big.txt").getFile(), "UTF-8");
            String sink = URLDecoder.decode(this.getClass().getResource("/test/sink.txt").getFile(), "UTF-8");
            //    一. Guava的文件写入
            //    Guava的Files类中提供了几个write方法来简化向文件中写入内容的操作，下面的例子演示 Files.write(byte[],File)的用法。
            demoFileWrite(sink, "Hello World!");
            //    二.获得文件内容
            //    Files类提供了readLines方法可以方便的读取文件的内容，如下demo代码：
            demoFileRead(source);
            //    注意这里的readLines方法返回的是List<String> 的接口，这对于大文件处理是会有问题的。大文件处理可以使用readLines方法的另一个重载。
            // 下面的例子演示从一个大文件中逐行读取文本，并做行号计数。
            File testFile = new File(source);
            CounterLine counter = new CounterLine();
            Files.readLines(testFile, Charsets.UTF_8, counter);
            out.println(counter.getResult());
            // 这个readLines的重载，需要我们实现一个LineProcessor的泛型接口，在这个接口的实现方法processLine方法中我们可以对行文本进行处理，getResult方法可以获得一个最终的处理结果，这里我们只是简单的返回了一个行计数。
            ReadOnelineProcessor onelineProcessor = new ReadOnelineProcessor(200);
            Files.readLines(testFile, Charsets.UTF_8, onelineProcessor).forEach(System.out::println);
            //另外还有readBytes方法可以对文件的字节做处理，readFirstLine可以返回第一行的文本，Files.toString(File,Charset)可以返回文件的所有文本内容。
            // 三. 复制移动（剪切）文件
            // 在Guava中复制文件操作提供了一组的copy方法，我们看一个示例：
            demoSimpleFileCopy(source, sink);
            // Guava中移动文件使用move方法，用法和copy一样。
            // Files.move(new File(source), new File(sink));
            // 四. 比较文件内容
            //Guava中提供了Files.equal(File,File)方法来比较两个文件的内容是否完全一致，请看下面的示例：
            demoEqual(source, sink);
            // 五. 其他有用的方法
            //    Guava的Files类中还提供了其他一些文件的简捷方法。比如
            //    touch方法创建或者更新文件的时间戳。
            //    createTempDir()方法创建临时目录
            //Files.createParentDirs(File) 创建父级目录
            //    getChecksum(File)获得文件的checksum
            //    hash(File)获得文件的hash
            //    map系列方法获得文件的内存映射
            //    getFileExtension(String)获得文件的扩展名
            //    getNameWithoutExtension(String file)获得不带扩展名的文件名
            //    Guava的方法都提供了一些重载，这些重载可以扩展基本用法，我们也有必要去多了解一下，这些重载的方法。

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 演示向文件中写入字节流
     *
     * @param fileName 要写入文件的文件名
     * @param contents 要写入的文件内容
     */
    public void demoFileWrite(final String fileName, final String contents) {
        checkNotNull(fileName, "Provided file name for writing must NOT be null.");
        checkNotNull(contents, "Unable to write null contents.");
        final File newFile = new File(fileName);
        try {
            Files.write(contents.getBytes(), newFile);
        } catch (IOException fileIoEx) {
            System.out.println(Throwables.getStackTraceAsString(fileIoEx));
        }
    }

    /**
     * 获得文件内容
     *
     * @param filePath
     */
    public void demoFileRead(final String filePath) {
        checkNotNull(filePath, "Provided file path for writing must NOT be null.");
        try {
            File testFile = new File(filePath);
            List<String> lines = Files.readLines(testFile, Charsets.UTF_8);
            lines.forEach(System.out::println);
        } catch (IOException e) {
            System.out.println(Throwables.getStackTraceAsString(e));
        }
    }

    /**
     * 演示如何使用guava的Files.copy方法复制文件
     *
     * @param sourceFileName 复制的源文件名
     * @param targetFileName 目标文件名
     */
    public void demoSimpleFileCopy(final String sourceFileName, final String targetFileName) {
        checkNotNull(sourceFileName, "Copy source file name must NOT be null.");
        checkNotNull(targetFileName, "Copy target file name must NOT be null.");
        final File sourceFile = new File(sourceFileName);
        final File targetFile = new File(targetFileName);
        try {
            Files.copy(sourceFile, targetFile);
        } catch (IOException fileIoEx) {
            err.println(
                    "ERROR trying to copy file '" + sourceFileName
                            + "' to file '" + targetFileName + "' - " + fileIoEx.toString());
        }
    }

    /**
     * 演示 Files.equal(File,File) 来比较两个文件的内容
     *
     * @param fileName1 比较的文件1文件名
     * @param fileName2 比较的文件2文件名
     */
    public void demoEqual(final String fileName1, final String fileName2) {
        checkNotNull(fileName1, "First file name for comparison must NOT be null.");
        checkNotNull(fileName2, "Second file name for comparison must NOT be null.");
        final File file1 = new File(fileName1);
        final File file2 = new File(fileName2);
        try {
            out.println(
                    "File '" + fileName1 + "' "
                            + (Files.equal(file1, file2) ? "IS" : "is NOT")
                            + " the same as file '" + fileName2 + "'.");
        } catch (IOException fileIoEx) {
            err.println(
                    "ERROR trying to compare two files '"
                            + fileName1 + "' and '" + fileName2 + "' - " + fileIoEx.toString());
        }
    }


    static class CounterLine implements LineProcessor<Integer> {
        private int rowNum = 0;

        @Override
        public boolean processLine(String line) throws IOException {
            rowNum++;
            return true;
        }

        @Override
        public Integer getResult() {
            return rowNum;
        }
    }


    static class ReadOnelineProcessor implements LineProcessor<List<String>> {
        private int lineNum = 1;//要读的行数
        private int rowNum = 0;
        private List<String> resultList = new ArrayList<>(2);

        public ReadOnelineProcessor(int lineNum) {
            Preconditions.checkArgument(lineNum>0,"lineNum should be giter than 0");
            this.lineNum = lineNum;
        }

        @Override
        public boolean processLine(String line) throws IOException {
            rowNum++;
            if (lineNum == rowNum) {
                resultList.add(lineNum + "");
                resultList.add(line);
                return false;
            }
            return true;
        }

        @Override
        public List<String> getResult() {
            return resultList;
        }
    }
}
